package tracingservice

import (
	"time"
	"alarmservice/model"
	"github.com/kaixinmao/cronexpr"
	"github.com/astaxie/beego"
)

//定时监控计划任务处理
type TimerTracingSchedule struct {
	TimeTracingIdsMap map[string][]int64
	LastTriggerTimes map[int64]time.Time
	stop chan bool
}

var TTSchedule TimerTracingSchedule

//只能运行一次
func (tts *TimerTracingSchedule) run() {
	go func() {
		tts.stop = make(chan bool)
		tick := time.Tick(60 * time.Second)//每分钟确认一次
		for {
			select {
			case <-tick:
				tts.CheckTracings(time.Now())
			case <-tts.stop:
				return
			}
		}
	}()
}

func (tts *TimerTracingSchedule) LoadTimerTracings() {
	offset := 0
	step := 1000
	timeNow := time.Now()
	
	for {
		modelTracings := make(model.MapTracing)
		model.Orm.Limit(step, offset).OrderBy("id desc").Where("`status`=? AND `type`=?", model.TracingStatusEnabled, model.TracingTypeTimer).Find(modelTracings)
		beego.Debug(modelTracings)
		if len(modelTracings) <= 0 {
			break
		}
		
		for _, v := range modelTracings {
			tracing, err := NewTimerTracing(v)
			if err != nil {
				panic(err)
			}
			tts.SetTracingCheckTime(tracing.Cronexpr.Next(timeNow), v.Id)
		}
		
		//设置最后一次报警时间
		tracingIds := modelTracings.GetTracingIds()
		
		modelTracingLastTriggerTimes := make(model.MapTracingLastTriggerTime)
		model.Orm.In("tracing_id", tracingIds).Find(modelTracingLastTriggerTimes)
		
		for _, tracingId := range tracingIds {
			if v, ok := modelTracingLastTriggerTimes[tracingId]; ok {
				tts.LastTriggerTimes[tracingId] = v.LastTriggerTime
			} else {
				tts.LastTriggerTimes[tracingId] = timeNow
			}
		}
		
		offset = offset + step
	}
}

//获取运行状态信息
func (tts *TimerTracingSchedule) GetStates() map[string]interface{} {
	states := make(map[string]interface{})
	//nextchecktime
	states["lasttriggertimes_num"] = len(tts.LastTriggerTimes)
	states["timetracingidsmap_num"] = len(tts.TimeTracingIdsMap)
	
	return states
}

//触发检查，读取tracing信息
func (tts *TimerTracingSchedule) CheckTracings(now time.Time) {
	checkTimeStr := TimeToCheckTimeStr(now)
	beego.Info("TimerTracing check tracings ...")
	//不管什么时候退出都删除当前检查时间
	defer delete(tts.TimeTracingIdsMap, checkTimeStr)
	
	tracingIds, ok := tts.TimeTracingIdsMap[checkTimeStr]
	if !ok || len(tracingIds) <= 0 {
		return
	}
	
	beego.Debug("check tracing ids:", tracingIds)
	
	//获取modelTracings
	modelTracings := make(model.MapTracing)
	

	err := model.Orm.In("id", tracingIds).Find(&modelTracings)
	if err != nil {
		beego.Error("Get Tracing error:" + err.Error())
		return
	}
	
	timeNow := time.Now()

	for TracingId, modelTracing := range modelTracings {
		tracing, err := NewTimerTracing(modelTracing)
		if err != nil {
			beego.Error("check tracing error" + err.Error())
			continue
		}
		
		if tracing.Model.Status != model.TracingStatusEnabled {
			tracing.DoDisabled()//检测到已经关闭监控了
			continue
		}
		
		//设置下次触发时间
		nextCheckTime := tracing.Cronexpr.Next(timeNow)
		tts.SetTracingCheckTime(nextCheckTime, tracing.Id)
		
		ltt, ok := tts.LastTriggerTimes[TracingId]
		if !ok {
			//没有这个值，不做检查，下次做检查
			tts.LastTriggerTimes[TracingId] = timeNow
		} else {
			//检查
			fromNextCheckTime := tracing.Cronexpr.Next(ltt)
			if !fromNextCheckTime.Equal(nextCheckTime) {
				fromNextCheckTime = tracing.Cronexpr.Next(fromNextCheckTime)
			}
			
			//上次触发时间的NextTime应该是当前检查时间，所以需要执行两次
			if fromNextCheckTime.Before(nextCheckTime) {
				//保存warning信息
				err := tracing.saveNewWarning("定时检查报警!")
				if err != nil {
					continue
				}
				
				//证明没有正确触发,发送报警信息
				err = tracing.SendWarningMsg()
				if err != nil {
					beego.Error(err)
					continue
				}
				
				
			}
		}
	} //end for modelTracings
}

func (tts *TimerTracingSchedule) SetTracingCheckTime(t time.Time, tracingId int64) {
	checkTimeStr := TimeToCheckTimeStr(t)
	tracingIds, ok := tts.TimeTracingIdsMap[checkTimeStr]
	if !ok || len(tracingIds) <= 0{
		tts.TimeTracingIdsMap[checkTimeStr] = []int64{tracingId}
		return
	}
	
	//检查是否存在相同的tracingId
	for _, v := range tracingIds {
		if v == tracingId {
			return
		}
	}
	
	tts.TimeTracingIdsMap[checkTimeStr] = append(tracingIds, tracingId)
}

func TimeToCheckTimeStr(t time.Time)  string {
	return t.Format("200601021504")
}



// *********** timertracing ***************

type TimerTracing struct {
	Tracing
	Cronexpr *cronexpr.Expression
}


func NewTimerTracing(modelTracing *model.Tracing) (*TimerTracing, error) {
	t := new(TimerTracing)
	t.Id = modelTracing.Id
	t.Model = modelTracing
	var err error
	t.Cronexpr, err = cronexpr.Parse(modelTracing.TracingTime)
	if err != nil {
		return nil, err
	}
	
	return t, nil
}

func (t *TimerTracing) Trigger(data string) error {
	timeNow := time.Now()
	timeNowStr := timeNow.Format("2006-01-02 15:04:05")
	TTSchedule.LastTriggerTimes[t.Id] = timeNow
	//修改数据库数据
	sql := "INSERT INTO `tracing_last_trigger_time` (`tracing_id`, `last_trigger_time`) VALUES(?, ?) ON DUPLICATE KEY UPDATE `last_trigger_time`=?"
	
	_, err := model.Orm.Exec(sql, t.Id, timeNowStr, timeNowStr)
	
	if err != nil {
		return err
	}
	return nil
}


func (t *TimerTracing) DoDisabled() error {	
	delete(TTSchedule.LastTriggerTimes, t.Id)
	return nil
}

func (t *TimerTracing) DoEnabled() error {
	tltt := &model.TracingLastTriggerTime{}
	
	err := model.GetById(t.Id, tltt)
	timeNow := time.Now()
	if err != nil {
		TTSchedule.LastTriggerTimes[t.Id] = timeNow
	} else {
		TTSchedule.LastTriggerTimes[t.Id] = tltt.LastTriggerTime
	}
	
	TTSchedule.SetTracingCheckTime(t.Cronexpr.Next(timeNow), t.Id)
	return nil
}

func (t *TimerTracing) DoDeleted() error {
	t.DoDisabled()
	return nil
}